#coding: utf-8

# Copyright (c) P SOFTHOUSE. All rights reserved.

import maya.cmds as cmds  # pylint: disable=E0401
import maya.mel as mel  # pylint: disable=E0401
import maya.api.OpenMaya as OpenMaya  # pylint: disable=E0401
import json
import os
import shutil
import filecmp
import webbrowser

from PencilLineGuiUtils import *
import PencilLineLineList as LineList
import PencilLineCommonFunctions as PclCommon
import pencil4lineRender

#
# MEMO
#
# Melからpython()コマンドで起動された場合、py内でimportしているモジュールは辞書登録されない。（dir() しても出てこない）
#
# Menuコマンドを設定する場合は、その中でimportしてやる必要があります。
# （もしくはMel側であらかじめpython("import XXXX")しておく）
#



# Pencil+のロゴ画像 ("About"ダイアログに使う)
logoFileName = "PencilLogo.png"

#
# PencilLineCommonFunctions.melから起動されます
#
def addMenu():
    # Check that the plugin successfully loaded
    if not cmds.pluginInfo('Pencil4', query=True, loaded=True):
        cmds.error('Error: Pencil4 plugin failed to load. Please try again.')

    cmds.menu("pencilPlusMenu", label="Pencil+ 4", parent="MayaWindow", tearOff=True)
    cmds.setParent("pencilPlusMenu", menu=True)
    licenseMode = mel.eval("pcl4license -mode")

    if(licenseMode != "Network Rendering"):
        cmds.menuItem(label=getLocalizedString("kOpenPencilLineWindow"),
                      command="PSOFT.PencilLineMainMenu.openPencilLineWindow()")
        cmds.menuItem(label=getLocalizedString("kAddSelectedEdges"),
                      command="PSOFT.PencilLineMainMenu.addSelectedEdges()")
        cmds.menuItem(label=getLocalizedString("kDeleteUnusedNodes"),
                      command="PSOFT.PencilLineMainMenu.deleteUnusedNodes()")
        cmds.menuItem(label=getLocalizedString("kPencilRenderSettings"),
                      command="PSOFT.PencilLineMainMenu.openPencilRenderSetting()")
        try:
            import Pcl4Bridge.InsertBridgeMenu
            Pcl4Bridge.InsertBridgeMenu.createBridgeMenu()
        except ImportError:
            pass
        cmds.menuItem(divider=True)
        cmds.menuItem(label=getLocalizedString("kPencilLineMenuManual"),
                      command="PSOFT.PencilLineMainMenu.openManual()")
        cmds.menuItem(label=getLocalizedString("kPencilLineMenuSampleFiles"),
                      command="PSOFT.PencilLineMainMenu.openSampleFiles()")

    if(licenseMode == "Standalone License"):
        cmds.menuItem(label=getLocalizedString("kPencilLineMenuAuthorize"),
                      command="import PSOFT.PencilLineAuthorizeDialogStandalone; PSOFT.PencilLineAuthorizeDialogStandalone.showAuthorizeDialog()")
    elif(licenseMode == "Network License"):
        cmds.menuItem(label=getLocalizedString("kPencilLineMenuAuthorize"),
                      command="import PSOFT.PencilLineAuthorizeDialogNetwork; PSOFT.PencilLineAuthorizeDialogNetwork.showAuthorizeDialog()")
    elif(licenseMode == "Demo"):
        pass

    cmds.menuItem(label=getLocalizedString("kPencilLineMenuAbout"),
        command="PSOFT.PencilLineMainMenu.showAboutDialog()")
    cmds.menuItem(divider=True)
    cmds.menuItem(label=getLocalizedString("kPencilLineMenuWebsite"),
        command="PSOFT.PencilLineMainMenu.openWebsite()")


def removeMenu():
    if cmds.menu("MayaWindow|pencilPlusMenu", ex=True):
        cmds.deleteUI("MayaWindow|pencilPlusMenu", menu=True)


"""
    Open Pencil+ 4 Line Window
"""
def openPencilLineWindow():
    LineList.showWindow()

"""
    Open the manual
"""
def openManual():
    mod_path = os.path.normpath(getModulePath())
    man_file = getLocalizedString("kManualFileName")
    man_path = os.path.join(mod_path, 'doc', man_file)

    temppath = None
    try:
        try:
            temppath = os.environ['temp']
        except KeyError:
            temppath = os.environ['tmpdir']
    except:
        pass
    if temppath:
        dst_path = os.path.join(temppath, man_file)
        if not os.path.exists(dst_path) or not filecmp.cmp(man_path, dst_path):
            shutil.copyfile(man_path, dst_path)
        if os.path.exists(dst_path):
            man_path = dst_path;

    os.startfile(man_path)

"""
    Open the sample files folder
"""
def openSampleFiles():
    path = os.path.normpath(getModulePath())
    os.startfile(path + '/SampleFiles')

"""
    Open the PSOFT website
"""
def openWebsite():
    URL = getLocalizedString('kPsoftUrl')
    # Open in the user's default browser and request a new window
    webbrowser.open(URL, new=1)

"""
    Open the Pencil Render Settings
"""
def openPencilRenderSetting():
    reder_settings = cmds.ls(type='PencilRenderSetting')
    if reder_settings:
        cmds.select(reder_settings[0])
    else:
        node = cmds.shadingNode('PencilRenderSetting', asUtility=True, name='PencilRenderSetting')
        cmds.setAttr(node + '.enabledSmoothMesh', 1)
        #cmds.lockNode(node, ln=True)

"""
    *********** Selected Edge operations *****************
"""

"""
    Add selected edges nodes
"""
def findShapeNode(selEdgeNode):
    node = selEdgeNode
    shape = cmds.connectionInfo(selEdgeNode + '.' + "meshReference", sourceFromDestination=True)
    return PclCommon.nodeNameFromAttributeName(shape)

kSEL_EDGE_ATTRNAME = 'PencilSelEdges'

"""
    Create a selected edge node for the specified shape and set.
"""
def createEdgeNode(shape, setName):
    # Create the selected edge node
    selEdgeNode = cmds.createNode('PencilSelectedEdge', skipSelect=True)

    # Get the mesh from the shape
    selectionList = OpenMaya.MSelectionList()
    selectionList.add(shape)
    object = selectionList.getDependNode(0)
    fnMesh = OpenMaya.MFnMesh(object)

    # Create 2 new UV sets
    try:
        uvName1 = fnMesh.createUVSet(selEdgeNode + 'UVSet1')
        uvName2 = fnMesh.createUVSet(selEdgeNode + 'UVSet2')
    except RuntimeError:
        # The shape probably did not support UV sets (for example: nurbs).
        # Delete the Selected Edge node and return silently.
        cmds.delete(selEdgeNode)
        return ''

    # Register the names of the UV sets
    cmds.setAttr(selEdgeNode + '.uvSet1Name', uvName1, type="string")
    cmds.setAttr(selEdgeNode + '.uvSet2Name', uvName2, type="string")

    addMultiMessageAttr(setName, kSEL_EDGE_ATTRNAME)
    # Add a link to the set
    # Set node does not allow using "connectAttr" with "nextAvailable" option.
    connectToDstNextFreeMultiIndex(selEdgeNode + '.message', setName + '.' + kSEL_EDGE_ATTRNAME)

    # Add the reference to the shape
    cmds.connectAttr(shape + '.message', selEdgeNode + '.meshReference')

    # Insert the node after the shape.
    # We used to connect to inMesh because we couldn't get dirty callbacks from 
    # outMesh when applying "smooth" to the mesh.
    # However, connecting to inMesh causes problems when saving the file,
    # such as the coordinates of the shape changing when we save (see Bug Track #252)
    # or the mesh not being saved (BT #581).
    # We retested using outMesh and could not reproduce the problem with the dirty
    # callbacks (maybe something else changed that fixed the problem), so we are
    # switching to inMesh, but should stay vigilant for the dirty problem
    # coming back.
    # This operation should cause the edge data to be computed one last time with
    # all connections made, so leave it at the end.
    cmds.connectAttr(shape + '.outMesh', selEdgeNode + '.mesh')

    return selEdgeNode

"""
    Process a single set for Selected Edges.
"""
def selEdgesProcessSet(setName):
    # Save the current selection
    initialSelection = cmds.ls(selection=True)

    # Get all the shapes connected to the set
    shapes = set() # Set makes it easy to avoid duplicates
    at = cmds.listAttr(setName + '.dagSetMembers', multi=True)

    # Nothing to do if the set is empty and running 'for' with a 'None' value causes an error.
    if at is None:
        return

    for a in at:
        nodeAttr = cmds.connectionInfo(setName + '.' + a, sourceFromDestination=True)
        node = PclCommon.nodeNameFromAttributeName(nodeAttr)
        # Sets may contain shapes (mesh) and transforms.
        # Use the shapes directly or get the shapes from the transforms.
        if ('mesh' == cmds.nodeType(node)):
            shapes.add(node)
        else:
            sh = cmds.listRelatives(node, shapes=True, noIntermediate=True)
            if sh is None: #Result is None for unsupported types (such as Nurbs)
                continue

            for s in sh:
                shapes.add(s)

    # Create Pcl4SelectedEdgeNode for each shape unless it already exists
    selEdgeNodes = cmds.ls(type='PencilSelectedEdge')
    for shape in shapes: # There are no duplicates in shapes
        selEdgeNode = ''
        # Find if a Pcl4SelectedEdgeNode already exists for this shape
        for s in selEdgeNodes:
            if (findShapeNode(s) == shape):
                selEdgeNode = s
                break

        if ('' == selEdgeNode):
            # This shape doesn't have a PencilSelectedEdge node. Create it.
            # We ignore the return value. If the operation fails, we simply
            # move on to the next shape.
            createEdgeNode(shape, setName)

        else:
            # We found a PencilSelectedEdge node. Connect it to the set if it is not already.
            found = False
            msgAttr = selEdgeNode + '.message'
            dsts = cmds.connectionInfo(msgAttr, destinationFromSource=True)
            for d in dsts:
                if (PclCommon.nodeNameFromAttributeName(d) == setName):
                    found = True
                    break

            if not found:
                addMultiMessageAttr(setName, kSEL_EDGE_ATTRNAME)
                connectToDstNextFreeMultiIndex(msgAttr, setName + '.' + kSEL_EDGE_ATTRNAME)
                
            # Recreate the UV sets if they were deleted
            # Get the mesh from the shape
            selectionList = OpenMaya.MSelectionList()
            selectionList.add(shape)
            obj = selectionList.getDependNode(0)
            fnMesh = OpenMaya.MFnMesh(obj)
            
            meshUVs = fnMesh.getUVSetNames()
            
            uvName = cmds.getAttr(selEdgeNode + '.uvSet1Name')
            if not uvName in meshUVs:
                newName = fnMesh.createUVSet(selEdgeNode + 'UVSet1')
                # Register the new name of the UV set
                cmds.setAttr(selEdgeNode + '.uvSet1Name', newName, type="string")
            
            uvName = cmds.getAttr(selEdgeNode + '.uvSet2Name')
            if not uvName in meshUVs:
                newName = fnMesh.createUVSet(selEdgeNode + 'UVSet2')
                # Register the new name of the UV set
                cmds.setAttr(selEdgeNode + '.uvSet2Name', newName, type="string")

    # Restore the initial selection
    cmds.select(initialSelection, replace=True)

"""
    Add the selected sets to Selected edges.
    This will create node construction history for target shape nodes that
    don't already have history.
    Undo support is not implemented yet.
"""
def addSelectedEdges():
    # The whole operation should be implemented as MPxCommand to support
    # Undo / Redo. Reference: http://ewertb.soundlinker.com/api/api.022.php
    # We could also implement it as multiple MPxCommands and use chunks to group them
    # all.
    #cmds.undoInfo(openChunk=True)
    #try:
    #CODE CODE CODE
    #finally:
    #cmds.undoInfo(closeChunk=True)

    # Get all selected sets
    selSets = cmds.ls(sets=True, selection=True)
    for s in selSets:
        selEdgesProcessSet(s)

"""
    *********** End Selected Edge operations *****************
"""

"""
    Delete unused Pencil Line nodes
"""
def deleteUnusedNodes():
    mel.eval("source PencilDeleteUnused")
    mel.eval("PencilDeleteUnused_deleteUnused()")
    for s in cmds.ls(set=True):
        deleteNolinkedMultiAttr(s, kSEL_EDGE_ATTRNAME)


"""
    "About" Dialog
"""
def showAboutDialog():
    print 'Pencil+ 4 Line Render Port :{}'.format(pencil4lineRender.listen.currentPort())
    cmds.layoutDialog(title="About PSOFT Pencil+ 4 for Maya", ui=aboutDialogUi)


def aboutDialogUi():
    fixDialogSize()
    form = cmds.setParent(q=True)
    cmds.formLayout(form, e=True, width=400, height=200)

    edition_str = mel.eval("pcl4license -mode")
    if edition_str == "Open Beta":
        mel.eval("pcl4license -reload")
        mel.eval('refreshEditorTemplates')
        limit = mel.eval("pcl4license -limit")
        if limit:
            edition_str += "\n" + getLocalizedString("kOpenBetaTimeLimit") + " "
            edition_str += limit[:4] + "/" + limit[4:6] + "/" + limit[6:]
        else:
            edition_str += "\n" + getLocalizedString("kOpenBetaExpired")

    image = cmds.picture(image=logoFileName)
    version = cmds.text(label="Version " + mel.eval("pcl4getinfo version") + "  Build " + mel.eval("pcl4getinfo buildNumber"))
    edition = cmds.text(label=edition_str)
    copyright = cmds.text(label=mel.eval("pcl4getinfo copyright"))

    okButton = cmds.button(label="OK", c='maya.cmds.layoutDialog(dismiss="OK")')

    cmds.formLayout(form,
        edit=True,
        attachForm=
        [(image, "top", 15),
         (image, "left", 15),
         (image, "right", 15),
         (version, "left", 5),
         (version, "right", 5),
         (edition, "left", 5),
         (edition, "right", 5),
         (copyright, "left", 5),
         (copyright, "right", 5),
         (okButton, "right", 150),
         (okButton, "left", 150),
         (okButton, "bottom", 15),
        ],
        attachControl=
        [(version, "top", 10, image),
         (edition, "top", 10, version),
         (copyright, "top", 10, edition),
         (okButton, "top", 20, copyright),
        ]
        )


def addMultiMessageAttr(nodeName, attrName):
    print nodeName
    print attrName
    if not mel.eval('attributeExists "{}" "{}"'.format(attrName, nodeName)):
        cmds.addAttr(nodeName, longName=attrName, at='message', multi=True)

def deleteNolinkedMultiAttr(nodeName, attrName):
    if mel.eval('attributeExists "{}" "{}"'.format(attrName, nodeName)):
        indices = cmds.getAttr(nodeName + '.' + attrName, multiIndices=True)
        isLinked = False
        for i in indices:
            if cmds.connectionInfo('{}.{}[{}]'.format(nodeName, attrName, i), sourceFromDestination=True):
                isLinked = True
                break
            if cmds.connectionInfo('{}.{}[{}]'.format(nodeName, attrName, i), destinationFromSource=True):
                isLinked = True
                break
        if not isLinked:
            cmds.deleteAttr(nodeName, at=attrName)

def connectToDstNextFreeMultiIndex(srcAttr, dstAttr):
    # We need to use getNextFreeMultiIndex (only available in MEL) to find the next
    # free index of nodes that do not allow using "connectAttr" with "nextAvailable" option.
    idx = getNextFreeMultiIndex(dstAttr, 0)
    if idx < 0:
        cmds.error('Could not find a free multi-index for attribute ' + dstAttr + '.')

    cmds.connectAttr(srcAttr, dstAttr + '[' + str(idx) + ']')

# Find the next unconnected multi index starting at the passed in index.
# Since MEL's getNextFreeMultiIndex is not available in Python, we rewrote it in Python.
# Basically the same code as the MEL version.
def getNextFreeMultiIndex(attrName, startIndex):
    # assume a max of 10 million connections
    while startIndex < 10000000:
        if len(cmds.connectionInfo('{}[{}]'.format(attrName, startIndex),
            sourceFromDestination=True) or []) == 0:
            return startIndex
        startIndex += 1

    # All connections full???
    return -1
